package trab4;

import java.util.* ;

import sim.core.*;
import static sim.core.Simulation.* ;

import trab4.rpc.*;
import trab4.transactions.*;

public class Client extends Node {
	
	TGridOperations tops ;
	
	public Client() {
		super() ;
	}
	
	public void init() {

		new PeriodicTask( rg.nextDouble(), 50) {
			public void run() {
				try {

					if (rg.nextDouble() < 0.02)
						doBlank();
					else
						doDistributedCircle();

				} catch( Exception x ){}
				;
			}
		};
	}

	
	void doBlank() {
		TGridOperations tops = Naming.lookupServer( this, "//" + rg.nextInt( ServerDB.size() ) ) ;		

		long tid = tops.c_OpenTransaction()  ;

		int[] gs = tops.gridSize(tid) ;

		int c = rg.nextInt() >>> 1 ; //make positive
		for( int i = 0 ; i < gs[0] ; i ++ )
			for( int j = 0 ; j < gs[1] ; j++ ) {
				tops.writeColor(tid, i, j, c ) ;
				tops.writeShape(tid, i, j, c ) ;
			}
		TransactionResult res = tops.c_CloseTransaction( tid) ; ;
		if (Main.DEBUG) System.out.println("Exit: doBlank: "  + res ) ;
		
	}
	
	void doCentralizedCircle() {
		TGridOperations tops = Naming.lookupServer( this, "//" + rg.nextInt( ServerDB.size() ) ) ;		

		long tid = tops.c_OpenTransaction()  ;

		int[] gs = tops.gridSize(tid) ;

		int c = rg.nextInt() >>> 1 ; //make positive
		boolean inColor = rg.nextBoolean() ;
		for( int ij : circleCoords( gs ) ) {
			
			int i = ij >> 16, j = ij & 0xFFFF ;
			
			if( inColor )
				tops.writeColor( tid, i, j, c) ;			
			else
				tops.writeShape( tid, i, j, c) ;			
				
		}
		TransactionResult res = tops.c_CloseTransaction( tid) ; ;
		if (Main.DEBUG) System.out.println("Exit: doCentralizedCircle: "  + res ) ;
	}
	
	void doDistributedCircle() {
		
		TGridOperations[] tops = new TGridOperations[ ServerDB.size() ] ;
		for( int i = 0 ; i < tops.length ; i++ )
			tops[i] = Naming.lookupServer( this, "//" + i  ) ;		

		long tid = tops[0].d_OpenTransaction()  ;
		int[] gs = tops[0].gridSize(tid) ;

		int c = rg.nextInt() >>> 1 ; //make positive
		for( long sij : dCircleCoords( gs ) ) {
			int s = (int)(sij >>> 32) ;
			int j = (int)(sij & 0xFFFF) ;
			int i = (int)((sij >> 16) & 0xFFFF);
			tops[s].writeColor( tid, i, (int)j, c) ;			
		}
		
		// This is not (yet) a true distributed transaction.
		// With a true distributed trasaction, a single close would be made on the server where it was started...
		TransactionResult res = tops[0].d_CloseTransaction(tid) ;
		for( int i = 0 ; i < tops.length ; i++ ) {
//			TransactionResult res = tops[i].d_CloseTransaction( tid) ;
			if (Main.DEBUG) System.out.println("Exit: doDistributedCircle: " + res + " in server:" + i ) ;
		}  /**/
	}
	
	/**
	 * Produces a set of cell coordinates <i, j> for a circumference
	 * spanning a single grid (in a single server)
	 * @param gs size of the grid stored by each server
	 * @return
	 */
	private Set<Integer> circleCoords( int[] gs ) {
		Set<Integer> res = new HashSet<Integer>() ;
		
		int cX = rg.nextInt( gs[0] ), cY = rg.nextInt( gs[1] ) ;
		int R = rg.nextInt( gs[0] ) + 5 ;
		for( double angle = 0 ; angle < 6.29 ; angle += 0.001) {
			double x = cX + R * Math.cos( angle ), y = cY + R * Math.sin( angle) ;
			int i = (int) x, j = (int) y ;
			if( i >= 0 && i < gs[0] && j >= 0 && j < gs[1])
				res.add( i << 16 | j ) ;
		}
		return res;
	}
	
	
	/**
	 * Produces a set of cell coordinates <server, i, j> for a circumference
	 * spanning multiple servers
	 * @param gs size of the grid stored by each server
	 * @return
	 */
	private Set<Long> dCircleCoords( int[] gs ) {
		int S = (int) Math.ceil( Math.sqrt( ServerDB.size() ) ) ;
		Set<Long> res = new HashSet<Long>() ;
		
		int SI = S * gs[0], SJ = S * gs[1] ;
		
		int R = rg.nextInt( SI ) ;
		int cI = rg.nextInt( SI ), cJ = rg.nextInt( SJ ) ;
		for( double angle = 0 ; angle < 6.29 ; angle += 0.001) {
			double x = cI + R * Math.cos( angle ), y = cJ + R * Math.sin( angle) ;
			int i = (int) x, j = (int) y ;
			if( i >= 0 && i < SI && j >= 0 && j < SJ) {
				long si = i / gs[0] ; int sj = j / gs[1] ;
				long ci = i % gs[0] ; int cj = j % gs[1] ;
				res.add( (sj*S+si) << 32 | ci << 16 | cj ) ;
			}
		}
		return res;
	}
}
